package com.personal.system.filter;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.personal.common.result.Result;
import com.personal.common.result.ResultCodeEnum;
import com.personal.common.utils.JwtHelper;
import com.personal.common.utils.ResponseUtil;
import com.personal.system.custom.Constants;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

//认证解析过滤器
@Slf4j
public class TokenAuthenticationFilter extends OncePerRequestFilter {

    private RedisTemplate redisTemplate;

    public TokenAuthenticationFilter(RedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        log.info("uri:{}" , request.getRequestURI());

        // 由于每次请求都经过此过滤器，则在引加入追踪ID，放行的url就没了，所以去拦截器去做 LogInterceptor
//        String tid = UUID.randomUUID().toString(true);
//        MDC.put("TRACE_ID", tid);

        UsernamePasswordAuthenticationToken authentication = getAuthentication(request);
        if (null != authentication) {
            authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
            SecurityContextHolder.getContext().setAuthentication(authentication);
            chain.doFilter(request, response);
        } else {
            ResponseUtil.out(response, Result.build(ResultCodeEnum.PERMISSION, ResultCodeEnum.PERMISSION));
        }
    }

    @Override
    public void destroy() {
        MDC.clear();
    }

    private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
        // token置于header里
        String token = request.getHeader("token");
        if (StrUtil.isNotBlank(token)) {
            String userId = JwtHelper.getUser(token).get("userId").toString();
            String username = JwtHelper.getUser(token).get("username").toString();
            logger.info("username:" + username);
            if (StrUtil.isNotBlank(username)) {
                String key = Constants.LOGIN_USER_KEY + ":" + username;
                ValueOperations operations = redisTemplate.opsForValue();
                Object obj = operations.get(key);
                if (null == obj) return null;
                String authorities = obj.toString();
                // 如果有token就重新设置过期时间
                operations.set(key,authorities,60, TimeUnit.MINUTES);
                JSONObject object = JSON.parseObject(authorities);
                JSONObject sysUser = object.getJSONObject("sysUser");
                if (CollUtil.isEmpty(object)) return null;
                List<SimpleGrantedAuthority> authority = new ArrayList<>();
                List<Map> mapList = JSON.parseArray(object.getString("authorities"), Map.class);
                for (Map map : mapList) {
                    authority.add(new SimpleGrantedAuthority((String) map.get("authority")));
                }
                //根据userDetails构建新的Authentication
                UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(username, sysUser, authority);
                return authentication;
            }
        }

        return null;
    }
}
